import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import akka.actor.ActorRef;
import akka.actor.UntypedActor;

/**
 * Line of passengers to be scanned.
 * 
 * @author Peter-John Rowe
 * @author Matthew Allioto
 * @author Evan
 */
public class Queue extends UntypedActor {
	/** 
	 * Stock indentation for messages from this actor. 
	 */
	private final String INDENTATION = "  ";
	/**
	 * The line it belongs to.
	 */
	private final int lineIndex;
	/**
	 * The reference to the body scan in the line
	 */
	private ActorRef bodyScan;
	/**
	 * The reference to the baggage scan in the line
	 */
	private final ActorRef baggageScan;
	/**
	 * Queue of waiting passengers to be sent to the body scan
	 */
	private BlockingQueue<Passenger> queue;
	/**
	 * Most recent passenger that came from the Document Check
	 */
	private Passenger mostRecent;
	/**
	 * The state of body scan
	 */
	State state = State.READY;

	/** Encapsulates BodyScanner internal state */
	public enum State {
		READY,
		NOT_READY;
	}
	
	/**
	 * The Queue constructor.
	 * 
	 * @param index
	 * @param bagScan
	 */
	public Queue(int index,ActorRef bagScan) {
		this.lineIndex = index;
		this.baggageScan = bagScan;
		this.queue = new LinkedBlockingQueue<Passenger>();
	}

	/**
	 * Sets the reference to the body scan
	 * 
	 * @param bodyScan
	 */
	public void setBodyScan(ActorRef bodyScan) {
		this.bodyScan = bodyScan;
	}
	
	/**
	 * The passenger puts his baggage in the baggage scanner.
	 * 
	 * @param p
	 */
	public void sendMessageToBagScanner(Passenger p) {
		baggageScan.tell(p);
		System.out.format("%sQueue %d: %s baggage placed on scanner\n", 
				INDENTATION, lineIndex, p.getName());				
	}

	/**
	 * Adds passenger to the line.
	 * 
	 * @param p
	 */
	public void addPassengerToQueue(Passenger p) {
		// Adds passenger to queue
		queue.add(p);
		// Receive passenger printout
		System.out.format("%sQueue %d: %s arrives in line\n", 
				INDENTATION, lineIndex, p.getName());	
	}

	/**
	 * Response to messages receives.
	 * 
	 * @param message - message received
	 */
	public void onReceive(Object message) throws Exception {
		
		// When Passenger message is received
		if (message instanceof Passenger) {
			// Passenger places bag on scanner
			sendMessageToBagScanner((Passenger)message);
			// Stores the passenger
			mostRecent = (Passenger)message;
			
			// If the body scan is marked as ready
			if(state == State.READY) {
				bodyScan.tell(mostRecent);
			// If body scan is not marked as ready	
			} else {
				addPassengerToQueue(mostRecent);
			}
		}
		
		// When the next message is received
		if (message instanceof Next) {
			// If no one is waiting to be scanned
			if (queue.isEmpty()) {
				state = State.READY;
			// If someone is waiting
			} else {
				state = State.NOT_READY;
				bodyScan.tell(queue.take());
			}
		}

		// When Pill message is received
		if (message instanceof Pill) { 
			// Receive close printout
			System.out.format("%sQueue %d: Close received\n", INDENTATION, lineIndex);

			// Propagates the close message
			baggageScan.tell(new Pill());
			// Close printout propagation
			System.out.format("%sQueue %d: Close sent to security\n", INDENTATION, lineIndex);

			// If the queue has people in it send the people to the bodyScan
			while (!queue.isEmpty()) {
				bodyScan.tell(queue.poll());
			}
			
			bodyScan.tell(new Pill());
			// Close printout propagation
			System.out.format("%sQueue %d: Close sent to body scan\n", INDENTATION, lineIndex);

			// Closes actor
			getContext().stop();
			// Print out that the body scan closed
			System.out.format("%sQueue %d: Closed\n", INDENTATION, lineIndex);
		}
	}
} // Queue Class
